package com.smileus.service.impl;

import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.apache.log4j.Logger;

import com.smileus.common.CallInfo;
import com.smileus.common.Constants;
import com.smileus.common.Env;
import com.smileus.common.TryTimes;
import com.smileus.entity.ServiceRouter;
import com.smileus.integration.caller.SyncServiceCaller;
import com.smileus.integration.cluster.divert.CloseInterval;
import com.smileus.service.BaseService;
import com.smileus.service.ClusterService;
import com.smileus.util.EnvProperties;
import com.smileus.util.ReflectionUtil;

public class DefaultClusterServiceImpl extends BaseService implements ClusterService {
	private static final Logger logger = Logger.getLogger(DefaultClusterServiceImpl.class);
	private static String serviceCentreSessionFactory = EnvProperties.getProperty("ServiceCentreSessionFactory");
	// key为sysDomain
	private static final Map<String, List<ServiceRouter>> RUNNING_MAP = new HashMap<String, List<ServiceRouter>>();
	private static ServiceRouter cfgRouter = setHostPort(Env.getBean("serviceRouter", ServiceRouter.class));
	// key为sysDomain,维护区间信息
	private Map<String, Map<CloseInterval, String>> WEIGHT_MAP = new HashMap<String, Map<CloseInterval, String>>();
	// key为sysDomain,统计总权重，产生随机数用
	private static final Map<String, Integer> TOTAL_WEIGHT_MAP = new HashMap<String, Integer>();
	// 记录URL访问发生异常的次数
	private static final Map<String, TryTimes> TRY_TIMES_MAP = new HashMap<String, TryTimes>();

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.smileus.service.Aaaa#getCloseMapBySysDomain(java.lang.String)
	 */
	public Map<CloseInterval, String> getCloseMapBySysDomain(String sysDomain) {
		if (WEIGHT_MAP.containsKey(sysDomain)) {
			return WEIGHT_MAP.get(sysDomain);
		}
		throw new RuntimeException("找不到相关的区间Map 。");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.smileus.service.Aaaa#getListBySysDomain(java.lang.String)
	 */
	public List<ServiceRouter> getListBySysDomain(String sysDomain) {
		if (RUNNING_MAP.containsKey(sysDomain)) {
			return RUNNING_MAP.get(sysDomain);
		} else {
			// 如何防止恶意刷新?--dongheng
			// if (refreshRunningMap().containsKey(sysDomain)) {
			// return RUNNING_MAP.get(sysDomain);
			// }
			throw new RuntimeException("找不到相关的服务资源信息 。");
		}
	}

	// public ServiceRouter getCurtServiceRouter(){
	// return null;
	// }
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.smileus.service.Aaaa#getServiceRouterByPubAddress(java.lang.String)
	 */
	public ServiceRouter getServiceRouterByPubAddress(String pubAddress) {
		return SyncServiceCaller.callForEntity("servicecentre.serviceRouterService.getServiceRouterByPubAddress", ServiceRouter.class, pubAddress);
	}
	/**
	 * 根据host,port,comDomain,sysDmomain信息，查询ServiceRouter list
	 * 
	 * @param curtRouter
	 * @return
	 */
	private static final String SERVICE_ROUTER_HQL = " from ServiceRouter sr where sr.comDomain =:comDomain and sr.sysDomain =:sysDomain and sr.serviceHost = :serviceHost and sr.servicePort =:servicePort and sr.serviceType=:serviceType ";
	public List<ServiceRouter> queryByServiceRouter(ServiceRouter serviceRouter) {
		return  getHibernateDAO(serviceCentreSessionFactory).queryByValueBean(ServiceRouter.class, SERVICE_ROUTER_HQL, serviceRouter);
	}

	public ServiceRouter saveOrUpdateServiceRouter(ServiceRouter serviceRouter) {
		List<ServiceRouter> list = queryByServiceRouter(serviceRouter);
		if (list.size() == 0) {
			save(serviceRouter);
			return serviceRouter;
		}

		if (list.size() == 1) {
			ServiceRouter curtRouter = list.get(0);
			ReflectionUtil.copy(curtRouter, serviceRouter);
			update(curtRouter);
			return curtRouter;
		}

		throw new RuntimeException("表中配置数据存在问题，请检查：" + serviceRouter);
//		return SyncServiceCaller.callForEntity("servicecentre.serviceRouterService.saveOrUpdateServiceRouter", ServiceRouter.class, serviceRouter);
	}

	public int getTotalWeight(String sysDomain) {
		if (TOTAL_WEIGHT_MAP.isEmpty()) {
			refreshRunningMap();
		}
		if (TOTAL_WEIGHT_MAP.containsKey(sysDomain)) {
			return TOTAL_WEIGHT_MAP.get(sysDomain);
		}
		throw new RuntimeException("找不到相关的总权重 。");
	}

	public void clusterRefresh() {
		try {
			refreshRunningMap();// 刷新本机
			// 刷新其他机器
			List<ServiceRouter> list = queryOtherRunningService();
			Client client = null;//
			String wsAddress = null;
			for (ServiceRouter router : list) {
				wsAddress = getWsAddress(router);
				client = JaxWsDynamicClientFactory.newInstance().createClient(wsAddress);
				client.invoke("doService", "serviceRouterService.refreshRunningMap", "[]");
				logger.debug(router.getSysDomain() + "地址为：" + wsAddress + "集群服务已刷新");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public ServiceRouter getCurtServiceRouter() {
		return setHostPort(cfgRouter);
	}

	private static ServiceRouter setHostPort(ServiceRouter sRouter) {
		if (sRouter == null || sRouter.getPubAddress() == null) {
			throw new RuntimeException("sRouter数据错误.");
		}
		String pubAddress = sRouter.getPubAddress();
		try {
			URL url = new URL(pubAddress);
			sRouter.setServiceHost(url.getHost());
			sRouter.setServicePort(String.valueOf(url.getPort()));
		} catch (Exception e) {
			// TODO: handle exception
			throw new RuntimeException(e);
		}
		return sRouter;
	}

	/**
	 * 查询所有正在运行的服务
	 * 
	 * @return
	 */
	private static final String ALL_RUNNING_HQL = " from ServiceRouter sr where sr.serviceStatus = 1";

	public List<ServiceRouter> queryAllRunningService() {
		
		return getHibernateDAO(serviceCentreSessionFactory).queryByParam(ServiceRouter.class, ALL_RUNNING_HQL);
	}

	/**
	 * 查询其他正在运行的服务
	 * 
	 * @return
	 */
	private static final String OTHER_RUNNING_HQL = " from ServiceRouter sr where sr.serviceStatus = 1 and sr.pubAddress != ?";

	public List<ServiceRouter> queryOtherRunningService() {
		return getHibernateDAO(serviceCentreSessionFactory).queryByParam(ServiceRouter.class, OTHER_RUNNING_HQL, cfgRouter.getPubAddress());
	}

	public Map<String, List<ServiceRouter>> refreshRunningMap() {
		String sysDomain = null;
		List<ServiceRouter> mapList = null;
		List<ServiceRouter> allList = queryAllRunningService();
		for (ServiceRouter router : allList) {
			sysDomain = getSysDomain(router);
			if (RUNNING_MAP.containsKey(sysDomain)) {
				RUNNING_MAP.get(sysDomain).add(router);
			} else {
				mapList = new ArrayList<ServiceRouter>();
				mapList.add(router);
				RUNNING_MAP.put(sysDomain, mapList);
			}
		}
		// 维护系统的权重区间和总权重MAP
		Map<CloseInterval, String> intervalUrlMap = null;
		CloseInterval ci = null;
		for (Entry<String, List<ServiceRouter>> entry : RUNNING_MAP.entrySet()) {
			sysDomain = entry.getKey();
			for (ServiceRouter router : entry.getValue()) {
				synchronized (sysDomain) {
					if (WEIGHT_MAP.containsKey(sysDomain)) {
						int total = TOTAL_WEIGHT_MAP.get(sysDomain) + router.getWeight();
						TOTAL_WEIGHT_MAP.put(sysDomain, total);
						intervalUrlMap = WEIGHT_MAP.get(sysDomain);
						int max = getMaxValue(intervalUrlMap);
						ci = new CloseInterval(max + 1, max + router.getWeight());
					} else {
						TOTAL_WEIGHT_MAP.put(sysDomain, router.getWeight());
						intervalUrlMap = new ConcurrentHashMap<CloseInterval, String>();
						ci = new CloseInterval(1, router.getWeight());
						WEIGHT_MAP.put(sysDomain, intervalUrlMap);
					}
					intervalUrlMap.put(ci, router.getPubAddress());
					TRY_TIMES_MAP.put(router.getPubAddress() + Constants.WSDL, new TryTimes(router.getTryAgainTimes()));
				}
			}
		}
		return RUNNING_MAP;
	}

	public String selectUrl(String sysDomain, String suspendUrl) {
		String url = null;
		List<ServiceRouter> list = RUNNING_MAP.get(sysDomain);
		if (list == null || list.size() == 0) {
			throw new RuntimeException("选不到合适的URL。");
		}
		url = list.get(0).getPubAddress() + Constants.WSDL;
		if (url.equals(suspendUrl) == false) {
			return url;
		} else if (list.size() > 1) {
			return list.get(1).getPubAddress() + Constants.WSDL;
		}
		throw new RuntimeException("选不到合适的URL。");
	}

	public boolean incrementAndCheck(String url) {
		if (TRY_TIMES_MAP.containsKey(url)) {
			return TRY_TIMES_MAP.get(url).incrementAndCheck();
		}
		throw new RuntimeException("访问地址不存在配置信息。");
	}

	public int getMaxValue(Map<CloseInterval, String> map) {
		int max = 0;
		for (CloseInterval ci : map.keySet()) {
			if (max < ci.getMax()) {
				max = ci.getMax();
			}
		}
		return max;
	}

	public String getSysDomain(ServiceRouter router) {
		if (router == null || router.getComDomain() == null || router.getSysDomain() == null) {
			throw new RuntimeException("router配置信息有错。");
		}
		return router.getSysDomain();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.smileus.service.Aaaa#getSysDomain(java.lang.String)
	 */
	public String getSysDomain(String serviceId) {
		// 如果SERVICEID_MAP包含，则直接获取
		return getCallInfo(serviceId).getSysDomain();
	}

	public CallInfo getCallInfo(String serviceId) {
		// 如果不存在，加载到Map，再返回
		// 如果SERVICEID_MAP包含，则直接获取
		if (serviceId == null || serviceId.length() == 0) {
			throw new RuntimeException("serviceId不能为空.");
		}
		// crm.userService.add
		String[] array = serviceId.split("\\.");
		if (array.length != 3) {
			throw new RuntimeException("不合法的serviceId，请检查格式是否为:katongprod.xxService.save形式");
		}
		StringBuilder service = new StringBuilder(20);
		service.append(array[1]).append(".").append(array[2]);
		CallInfo callInfo = new CallInfo(array[0], service.toString());
		String sysDomain = callInfo.getSysDomain();
		if (RUNNING_MAP.isEmpty()) {
			refreshRunningMap();
		}
		List<ServiceRouter> list = RUNNING_MAP.get(sysDomain);
		ServiceRouter serviceRouter = null;
		if (list == null) {
			serviceRouter = cfgRouter;
		}else {
			serviceRouter = list.get(0);
		}
		callInfo.setServiceType(serviceRouter.getServiceType());// 如果不存在，让他抛错好了
		return callInfo;
	}

	public String getWsAddress(ServiceRouter router) {
		if (router == null || router.getPubAddress() == null) {
			throw new RuntimeException("请先配置pubAddress");
		}
		return router.getPubAddress() + Constants.WSDL;
	}

	public boolean sendMessage(ServiceRouter sRouter) {
		try {
			// TODO
			System.out.println("短信：服务IP=" + sRouter.getServiceHost() + "可能宕机了，请检查。");
			System.out.println("邮件：服务IP=" + sRouter.getServiceHost() + "可能宕机了，请检查。");
			return true;
		} catch (Exception e) {
			return false;
		}
	}
}
